package cn.ipokerface.api.parser;

import cn.ipokerface.api.ConfigurationHolder;
import cn.ipokerface.api.ResponseType;
import cn.ipokerface.api.Type;
import cn.ipokerface.api.model.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

/**
 * Created by       PokerFace
 * Create Date      2019-12-11.
 * Email:           <a href="mailto:214888341@163.com">214888341@163.com</a>
 * Version          1.0.0
 * <p>
 * Description:
 */
public class MarkdownParser implements AbstractParser {

    private Random random = new Random();


    @Override
    public void parse(ConfigurationHolder configurationHolder, OutputStream outputStream) throws IOException {

        writeTitleDescription(configurationHolder.getTitle(), configurationHolder.getDescription(), outputStream);

        writeDefines(configurationHolder.getDefineList(), outputStream);

        writeGroupApis(configurationHolder, configurationHolder.getGroupList(), configurationHolder.getApiList(), outputStream);

        writeEnums(configurationHolder.getEnumerationList(), outputStream);
    }


    private void writeTitleDescription(String title, String description, OutputStream outputStream)  throws IOException{
        if (title == null || description != null) return;

        write(new StringBuilder("").append("# ")
                .append(title)
                .append("\r\n\r\n")
                .append("## ")
                .append(description)
                .append("\r\n\r\n")
                .toString(), outputStream);

    }



    private void writeDefines(List<Define> defineList, OutputStream outputStream) throws IOException {
        if (defineList == null || defineList.size() == 0) return;

        write("## 常用参数定义 \r\n", outputStream);

        write("|参数名|参数值|\r\n",outputStream);
        write("|:--:|:--:|\r\n", outputStream);
        for(Define define: defineList) {
            write(new StringBuilder("")
                    .append(" | ")
                    .append(define.getName())
                    .append(" | ")
                    .append(define.getValue())
                    .append(" |\r\n")
                    .toString(), outputStream);
        }

        write("\r\n\r\n", outputStream);

    }

    private void writeGroupApis(ConfigurationHolder configurationHolder, List<Group> groups,List<SmartApi> apis, OutputStream outputStream) throws IOException{
        if (groups == null || groups.size()== 0) return;

        write("## 接口定义\r\n", outputStream);
        writeGroupApis(configurationHolder, groups, "### ", outputStream);

        writeApis(configurationHolder, apis, "### ", outputStream);

        write("\r\n\r\n", outputStream);
    }


    private void writeGroupApis(ConfigurationHolder configurationHolder, List<Group> groups, String levelString, OutputStream outputStream) throws IOException {


        Collections.sort(groups, new Comparator<Group>() {
            @Override
            public int compare(Group o1, Group o2) {
                return o1.getId() - o2.getId();
            }
        });

        int index = 1;
        for(Group group: groups) {
            write(new StringBuilder("")
                    .append(levelString)
                    .append(index)
                    .append(".  ")
                    .append(group.getName())
                    .append("\r\n")
                    .toString(), outputStream);

            if (group.getChildren()!= null && group.getChildren().size() > 0)
                writeGroupApis(configurationHolder, group.getChildren(), "#"+levelString, outputStream);

            if (group.getSmartApis() != null && group.getSmartApis().size() > 0)
                writeApis(configurationHolder, group.getSmartApis(), "#"+levelString, outputStream);
            index ++;
        }

    }


    private void writeApis(ConfigurationHolder configurationHolder, List<SmartApi> apiList, String levelString, OutputStream outputStream) throws IOException {
        if (apiList == null || apiList.size() == 0) return;

        int index = 1;
        for(SmartApi api: apiList) {
            write(new StringBuilder("")
                    .append(levelString)
                    .append("(")
                    .append(index)
                    .append(") ")
                    .append(api.getName())
                    .append("\r\n")
                    .toString(), outputStream);

            writeApi(configurationHolder,api, outputStream);
        }
    }


    private void writeApi(ConfigurationHolder configurationHolder, SmartApi api, OutputStream outputStream) throws IOException{
        write("+ (1) 接口基本信息\r\n", outputStream);
        write("|参数|参数值|\r\n",outputStream);
        write("|--:|:--|\r\n", outputStream);
        write("|Url|"+api.getUrl()+"|\r\n", outputStream);
        write("|Method|", outputStream);
        if(api.getMethod() != null && api.getMethod().length >= 0){
            for(int i=0;i<api.getMethod().length;i++) {
                if (i == 0){
                    write(api.getMethod()[i].name(), outputStream);
                }
                else{
                    write(","+api.getMethod()[i].name(), outputStream);
                }
            }
        }
        write("|\r\n", outputStream);
        write("|Response|"+api.getResponse().name()+"|\r\n", outputStream);

        write("+ (2) 请求头\r\n", outputStream);
        if(api.getHeaderList() != null && api.getHeaderList().size() > 0) {
            write("|Key|名字|可选值|\r\n",outputStream);
            write("|:--|:--:|:--|\r\n", outputStream);

            for(Header header: api.getHeaderList()) {
                StringBuilder builder = new StringBuilder("")
                        .append("|")
                        .append(header.getKey())
                        .append("|")
                        .append(header.getName())
                        .append("|");

                appendOptions(builder, header.getOptions(), configurationHolder);

                builder.append("|\r\n");
                write(builder.toString(), outputStream);
            }
        }

        write("+ (3) 请求参数\r\n", outputStream);
        write("|Key|名字|必传|类型|备注|可选值|\r\n",outputStream);
        write("|:--|:--:|:--:|:--:|:--|:--|\r\n", outputStream);

        if (api.getParameterList() == null || api.getParameterList().size() == 0) {
            write("|\t | | | | | |\r\n",outputStream);
        }else{
            writeApiParameter(configurationHolder, api, api.getParameterList(), "", outputStream);
        }


        write("+ (4) 响应数据\r\n", outputStream);
        write("|Key|名字|必含|类型|备注|可选值|\r\n",outputStream);
        write("|:--|:--:|:--:|:--:|:--|:--|\r\n", outputStream);

        if (api.getResponseList() == null || api.getResponseList().size() == 0) {
            write("|\t | | | | | |\r\n",outputStream);
        }else{
            JSONObject response = writeApiResponse(configurationHolder, api, api.getResponseList(), "", outputStream);
            if (ResponseType.Json.equals(api.getResponse())) {
                write("```\r\n", outputStream);
                write(JSON.toJSONString(response, SerializerFeature.PrettyFormat,
                        SerializerFeature.QuoteFieldNames,
                        SerializerFeature.WriteNullStringAsEmpty), outputStream);
                write("\r\n```\r\n", outputStream);
            }
        }


    }


    private void writeApiParameter(ConfigurationHolder holder, SmartApi api,
                                   List<Parameter> parameters,String parentKey, OutputStream outputStream) throws IOException{
        if(parameters == null || parameters.size() == 0) return;

        for(Parameter parameter: parameters){
            String key = parentKey == null || "".equals(parentKey)?(parameter.getKey()) : (parentKey + "." + parameter.getKey());

            StringBuilder builder = new StringBuilder("")
                    .append("|")
                    .append(key)
                    .append("|")
                    .append(parameter.getName())
                    .append("|")
                    .append(parameter.isRequired()?"True":"False")
                    .append("|")
                    .append(parameter.getType().name())
                    .append("|")
                    .append(parameter.getRemark())
                    .append("|");

            appendOptions(builder, parameter.getOptions(), holder);


            builder.append("|\r\n");
            write(builder.toString(), outputStream);

            writeApiParameter(holder, api, parameter.getProperties(), key, outputStream);

        }

    }


    private JSONObject writeApiResponse(ConfigurationHolder holder, SmartApi api,
                                   List<Response> responses,String parentKey, OutputStream outputStream) throws IOException{
        JSONObject jsonObject = new JSONObject();

        if(responses == null || responses.size() == 0) return jsonObject;

        for(Response response: responses){
            String key = parentKey == null || "".equals(parentKey)?(response.getKey()) : (parentKey + "." + response.getKey());

            StringBuilder builder = new StringBuilder("")
                    .append("|")
                    .append(key)
                    .append("|")
                    .append(response.getName())
                    .append("|")
                    .append(response.isContains()?"True":"False")
                    .append("|")
                    .append(response.getType().name())
                    .append("|")
                    .append(response.getRemark())
                    .append("|");

            appendOptions(builder, response.getOptions(), holder);


            builder.append("|\r\n");
            write(builder.toString(), outputStream);

            if (Type.Model.equals(response.getType()) || Type.ModelList.equals(response.getType())) {
                JSONObject childJson = writeApiResponse(holder, api, response.getProperties(), key, outputStream);
                if (Type.Model.equals(response.getType())) {
                    jsonObject.put(response.getKey(), childJson);
                }else{
                    JSONArray jsonArray = new JSONArray();
                    jsonArray.add(childJson);
                    jsonObject.put(response.getKey(), jsonArray);
                }
            }else if(Type.Int.equals(response.getType())){
                jsonObject.put(response.getKey(), random.nextInt());
            }else if(Type.Long.equals(response.getType())) {
                jsonObject.put(response.getKey(), random.nextLong());
            }else if (Type.Float.equals(response.getType())) {
                jsonObject.put(response.getKey(), random.nextFloat());
            }else if (Type.String.equals(response.getType())){
                jsonObject.put(response.getKey(), response.getKey());
            }else if (Type.IntList.equals(response.getType())) {
                JSONArray jsonArray = new JSONArray();
                jsonArray.add(random.nextInt());
                jsonArray.add(random.nextInt());
                jsonArray.add(random.nextInt());
                jsonObject.put(response.getKey(), jsonArray);
            }else if (Type.LongList.equals(response.getType())) {
                JSONArray jsonArray = new JSONArray();
                jsonArray.add(random.nextLong());
                jsonArray.add(random.nextLong());
                jsonArray.add(random.nextLong());
                jsonObject.put(response.getKey(), jsonArray);
            }else if (Type.FloatList.equals(response.getType())) {
                JSONArray jsonArray = new JSONArray();
                jsonArray.add(random.nextFloat());
                jsonArray.add(random.nextFloat());
                jsonArray.add(random.nextFloat());
                jsonObject.put(response.getKey(), jsonArray);
            }else if (Type.StringList.equals(response.getType())) {
                JSONArray jsonArray = new JSONArray();
                jsonArray.add(response.getKey()+"1");
                jsonArray.add(response.getKey()+"2");
                jsonArray.add(response.getKey()+"3");
                jsonObject.put(response.getKey(), jsonArray);
            }

            // Construct json

        }

        return jsonObject;

    }

    private void appendOptions(StringBuilder builder, String key, ConfigurationHolder holder){
        if (key != null && !"".equals(key) ) {
            Enumeration enumeration =holder.getEnumeration(key);
            if (enumeration != null && enumeration.getValues()!=null){
                for(int i=0;i<enumeration.getValues().size();i++) {
                    EnumerationValue value = enumeration.getValues().get(i);
                    builder.append(value.getKey()+":    "+ value.getName());
                    if(i != enumeration.getValues().size() - 1) {
                        builder.append("<br>");
                    }
                }
            }
        }
    }


    private void writeEnums(List<Enumeration> enumerations, OutputStream outputStream) throws IOException {
        if (enumerations == null || enumerations.size() == 0) return;


        write("## 枚举参数定义\r\n", outputStream);
        for(Enumeration enumeration: enumerations) {
            write("### "+enumeration.getKey()+"    "+enumeration.getName()+"\r\n", outputStream);
            if (enumeration.getValues() != null && enumeration.getValues().size() > 0){
                write("|参数|参数值|\r\n",outputStream);
                write("|:--:|:--:|\r\n", outputStream);
                for(EnumerationValue value: enumeration.getValues()) {
                    write(new StringBuilder("")
                            .append("|")
                            .append(value.getKey())
                            .append("|")
                            .append(value.getName())
                            .append("|\r\n")
                            .toString(), outputStream);
                }
            }
        }

    }



    private void write(String content,OutputStream outputStream) throws IOException{
        outputStream.write(content.getBytes("utf-8"));
    }
}
